﻿/*=====================================================================
屏幕的四条边中分为两个部分，加上四个角共十二个热区。
中键右键在热区中的点击或者中键的上下滚动可以激活相应的动作。
不在热区内的移动可以触发鼠标手势。
=======================================================================
*/

#Include lzShowTip.ahk
#Include lzGdi.ahk

手势 := MouseGesture()

class MouseGesture
{

    __New() {
        ; 创建一个画板来画轨迹，试过其他方式慢得不能接受
        this._board := Gui("+AlwaysOnTop -Caption +ToolWindow +E0x80000")
        ; 关联一个 GDI 对象
        this._gdi := GDI(this._board.hwnd, A_ScreenWidth, A_ScreenHeight)
        ; 鼠标手势需要的
        this.lastx := -1, this.lasty := -1, this.gesture := ""
        this.turnOn()
    }

    turnOn() {
        鼠标操作(key) => 手势.鼠标动作(key) 
        Hotkey("RButton",   鼠标操作, "On")
        Hotkey("MButton",   鼠标操作, "On")
        Hotkey("WheelUp",   鼠标操作, "On")
        Hotkey("WheelDown", 鼠标操作, "On")
        Hotkey("#g", 关闭鼠标增强(key) => this.turnOff(), "On")
        lzShowTip("鼠标增强已开启！可以按 win + g 关闭。",, 0xFF00FF)
    }

    turnOff() {
        Hotkey("RButton",   "off")
        Hotkey("MButton",   "off")
        Hotkey("WheelUp",   "off")
        Hotkey("WheelDown", "off")
        Hotkey("#g", 打开鼠标增强(key) => this.turnOn(), "On")
        lzShowTip("鼠标增强已关闭！按 win+g 重新开启。",, 0xFF00FF)
    }

    _lzGestureTips := Map(
                    "上", "上翻页 PgUp"
                  , "下", "下翻页 PgDn"
                  , "左", "后退 Browser_Back"
                  , "右", "前进 Browser_Forward"
                  , "左上", "恢复关闭的标签页 Ctrl+Shift+t"
                  , "左下", "关闭标签页 Ctrl+w"
                  , "右上", "退出 Esc"
                  , "右下", "回车 Enter"
                  , "上左", "上翻页 PgUp"
                  , "下左", "下翻页 PgDn"
                  , "上右", "到顶部 Home"
                  , "下右", "到底部 End"
                  , "上下", "关闭窗口 ALT+F4"
                  , "下上", "到底部 End"
                  , "左右", "回格键 Backspace"
                  , "右左", "切换标签 Tab"
                  , "左右左", "显示快捷菜单"
                  , "右左右", "显示快捷菜单"
                  , "左右左右", "关闭鼠标增强"
                  , "右左右左", "打开 Notepad2"
              )
    _lzGestureTips.default := "手势未定义操作"

    _lzGestureRun(gesture) {
        switch(gesture) {
            case "上": send("{PgUp}")
            case "下": send("{PgDn}")
            case "左": send("{Browser_Back}")
            case "右": send("{Browser_Forward}")
            case "左上": send("^+t")
            case "左下": send("^w")
            case "右上": send("{Esc}")
            case "右下": send("{Enter}")
            case "上左": send("{PgUp}")
            case "下左": send("{PgDn}")
            case "上右": send("{Home}")
            case "下右": send("{End}")
            case "上下": send("!{F4}")
            case "下上": send("{End}")
            case "左右": send("{Backspace}")
            case "右左": send("{Tab}")
            case "左右左": lzMenu2.showWin()
            case "右左右": lzMenu3.showWin()
            case "左右左右": this.turnOff()
            case "右左右左": run(envget("wsr") . "\Program\TotalCMD\Tools\Notepad2.exe")
        }
    }

    _lzHotAreaTips := Map(
             "左上角MButton", "刷新页面"
            , "上左MButton", "上一页"
            , "上右MButton", "下一页"
            , "右上角MButton", "停止载入"
            , "右上MButton", "上一页"
            , "右下MButton", "下一页"
            , "左下角MButton", "播放/暂停"
            , "下左MButton", "上一首"
            , "下右MButton", "下一首"
            , "右下角MButton", "停止播放"
            , "左上MButton", "上一首"
            , "左下MButton", "下一首"
                        )
    _lzHotAreaTips.default := ""

    _lzHotAreaRun(area, key) {
        switch(area . key) {
            case "左上角MButton": send "{Browser_Refresh}"
            case "上左MButton": send "{Browser_Back}"
            case "上右MButton": send "{Browser_Forward}"
            case "右上角MButton": send "{Browser_Stop}"
            case "右上MButton": send "{Browser_Back}"
            case "右下MButton": send "{Browser_Forward}"
            case "左下角MButton": send "{Media_Play_Pause}"
            case "下左MButton": send "{Media_Prev}"
            case "下右MButton": send "{Media_Next}"
            case "右下角MButton": send "{Media_Stop}"
            case "左上MButton": send "{Media_Prev}"
            case "左下MButton": send "{Media_Next}"
            ; 在热区滚动滚轮
            case "下左WheelUp", "左下WheelUp": send("{Volume_Up 1}")
            case "下左WheelDown", "左下WheelDown": send("{Volume_Down 1}")
            case "下右WheelUp", "左上WheelUp": send("{Volume_Up 5}")
            case "下右WheelDown", "左上WheelDown": send("{Volume_Down 5}")
            default: return
        }
    }

    鼠标动作(key) {
        ; 管理鼠标在四条边与四个角时的行为
        CoordMode("Mouse", "Screen")
        MouseGetPos(&x, &y)
        theArea := this._hotArea(x, y)
        if theArea {
            lzShowTip(this._lzHotAreaTips[theArea . key])
            this._lzHotAreaRun(theArea, key)
        } else {
            if (key == "RButton" || key == "MButton")
                this._lzGestureHandler(key)
            else
                send("{" key "}")       ; 滚轮滚动
        }
    }

    _lzGestureHandler(key) {
        lzGestureHandler(key)
        this._board.show("NA")
        lzGestureHandler(key) {
            mouseMov := this._lzGetMouseMov()
            if (mouseMov != SubStr(this.gesture, -1, 1)) {
                this.gesture := this.gesture . mouseMov
            }
            tooltip(this.gesture ? this.gesture . " ："
                    . this._lzGestureTips[this.gesture] : "")
            stillPressed := getKeyState(key, "P")
            if stillPressed {
                settimer(() => lzGestureHandler(key), -20)
            } else {
                this._clearpath()
                tooltip()
                if this.gesture {
                    this._lzGestureRun(this.gesture)
                } else {
                    send("{" key "}")
                }
                this.gesture := "", this.lastx := -1
            }
        }
    }

    _lzGetMouseMov() {
        CoordMode("Mouse", "Screen")
        MouseGetPos(&x, &y)
        if (this.lastx == -1) {
            this.lastx := x, this.lasty := y
            return
        }
        dx := x - this.lastx
        dy := this.lasty - y   ; 坐标原点是左上角
        dz := sqrt(dx * dx + dy * dy)  ; 用斜边以避免分母为零的情况
        da := sin(0.35)                ; 20 度的弧度 0.35 的正弦值作为比较的标准
        mouseAction := ""
        if (dz > 9) {                  ; 斜边大于此值才算动作
                this._gdi.DrawLine(x, y, this.lastx, this.lasty, 0xFF00FF, 6)
                this._gdi.UpdateLayeredWindow()
                this.lastx := x, this.lasty := y
            if (abs(dx/dz) < da) {
                mouseAction := dy > 0 ? "上" : "下"
            } else if (abs(dy/dz) < da) {
                mouseAction := dx > 0 ? "右" : "左"
            }
        }
        return mouseAction
    }

    _clearPath() {
        this._board.Hide()
        this._gdi.FillRectangle(0,0,this._gdi.CliWidth,this._gdi.CliHeight,0)
        this._gdi.UpdateLayeredWindow()
    }

    _hotArea(x, y) {
        ; 每条边中分成两个部分再加上四个角共十二个位置称为热点
        res := ""   ; 鼠标不在边角就返回空字符串
        right := A_ScreenWidth - 1
        halfWidth := right / 2
        bottom := A_ScreenHeight - 1
        halfHeight := bottom / 2
        if (x == 0) {
            if (y == 0) {
                res := "左上角"
            } else if (y < halfHeight) {
                res := "左上"
            } else if (y < bottom) {
                res := "左下"
            } else {
                res := "左下角"
            }
        } else if (x < halfWidth) {
            if (y == 0) {
                res := "上左" 
            } else if (y == bottom) {
                res := "下左"
            }
        } else if (x < right) {
            if (y == 0) {
                res := "上右" 
            } else if (y == bottom) {
                res := "下右"
            }
        } else {
            if (y == 0) {
                res := "右上角"
            } else if (y < halfHeight) {
                res := "右上"
            } else if (y < bottom) {
                res := "右下"
            } else {
                res := "右下角"
            }
        }
        return res
    }
}
